package com.hzj.rtsp.task;

import com.hzj.rtsp.dto.Video;
import org.apache.commons.io.FileUtils;
import org.bytedeco.ffmpeg.avcodec.AVPacket;
import org.bytedeco.ffmpeg.avformat.AVFormatContext;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;

import static org.bytedeco.ffmpeg.global.avcodec.av_packet_unref;

/**
 * Created by Hzj on 2021/5/20.
 */
public class HlsTasker implements Runnable {

    private Video video;
    private String  rtspurl;
    private String  m3u8FilePath;
    private String  m3u8FileDir;
    private static Logger logger	= LoggerFactory.getLogger( HlsTasker.class );

    /**
     * 60秒后没有播放请求，则转换线程停止
     * */
    private   final long duration=1000*30;
    //最后一次播放请求时间
    private     long lastPlayTime = System.currentTimeMillis();

    public void reloadPlayTime( ) {
        this.lastPlayTime = System.currentTimeMillis();
    }


    /**
     * 读流器
     */
    private FFmpegFrameGrabber grabber;
    /**
     * 转码器
     */
    private FFmpegFrameRecorder recorder;

    public HlsTasker(Video video, String url, String m3u8path){
        this.video=video;
        this.rtspurl=url;
        this.m3u8FileDir=m3u8path+"/"+video.getId();
        this.m3u8FilePath = m3u8path+"/"+video.getId()+"/index.m3u8";
        File file=new File(m3u8FileDir);
        if(!file.exists())
            file.mkdirs();

    }

    @Override
    public void run() {
            logger.info("m3u8FilePath={}",m3u8FilePath);
            //抓取
            int bitrate = 2500000;// 比特率
            double framerate;// 帧率
            int err_index = 0;// 推流过程中出现错误的次数
            int timebase;// 时钟基
            boolean add=false;

            try {
                // 开启ffmpeg日志级别；QUIET是屏蔽所有，可选INFO、DEBUG、ERROR等
                avutil.av_log_set_level(avutil.AV_LOG_ERROR);
                //FFmpegLogCallback.set();
                grabber = new FFmpegFrameGrabber(rtspurl);
                grabber.setOption("rtsp_transport", "tcp");
                //allowed_media_types设置要从服务器接受的媒体类型，为空默认支持所有媒体类型，支持的媒体类型：[video，audio，data]
                grabber.setOption("allowed_media_types", "video");
                /*
                 * rtsp_flags:[filter_src,prefer_tcp,listen]
                 * filter_src:仅接受来自协商对等地址和端口的数据包。
                 * prefer_tcp:如果TCP可用作RTSP RTP传输，请首先尝试使用TCP进行RTP传输。
                 * listen:充当rtsp服务器，监听rtsp连接
                 */
                grabber.setOption("rtsp_flags", "prefer_tcp");
                /*
                 * 设置等待传入连接最大超时时间（单位：秒），默认值-1无限等待
                 * 如果设置此选项，上面的rtsp_flags配置将被设置成“listen”，充当rtsp服务器，监听rtsp连接
                 */
                grabber.setOption("stimeout", "5000000");
                grabber.start();
                //设置过滤器内容，具体参考http://ffmpeg.org/ffmpeg-filters.html
               // FFmpegFrameFilter filter =new FFmpegFrameFilter("filterContent", grabber.getImageWidth(),grabber.getImageHeight());
               // filter.start();
                // 异常的framerate，强制使用25帧
              /*  if (grabber.getFrameRate() > 0 && grabber.getFrameRate() < 100) {

                    framerate = grabber.getFrameRate();
                } else {*/

                    framerate = 25.0;
                //}

                bitrate = grabber.getVideoBitrate();// 获取到的比特率 0

                recorder = new FFmpegFrameRecorder(m3u8FilePath, grabber.getImageWidth(), grabber.getImageHeight(), grabber.getAudioChannels());
                // 设置比特率
                recorder.setVideoBitrate(bitrate);
                // h264编/解码器
                recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
                // 设置音频编码
                recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
                // 视频帧率(保证视频质量的情况下最低25，低于25会出现闪屏)
                recorder.setFrameRate(framerate);
                // 关键帧间隔，一般与帧率相同或者是视频帧率的两倍
                recorder.setGopSize(2*(int) framerate);
                recorder.setVideoQuality(1.0); //视频质量
                recorder.setVideoBitrate(10*1024);//码率
                // 解码器格式
                recorder.setFormat("hls");
                // 单个切片时长,单位是s，默认为2s
                recorder.setOption("hls_time", "2");
                // HLS播放的列表长度，0标识不做限制
                recorder.setOption("hls_list_size", "12");
                // 设置切片的ts文件序号起始值，默认从0开始，可以通过此项更改
                recorder.setOption("start_number", "0");
                //自动删除切片，如果切片数量大于hls_list_size的数量，则会开始自动删除之前的ts切片，只保留hls_list_size个数量的切片
                recorder.setOption("hls_flags","delete_segments");
                //ts切片自动删除阈值，默认值为1，表示早于hls_list_size+1的切片将被删除
                recorder.setOption("hls_delete_threshold","1");

                /*hls的切片类型：
                 * 'mpegts'：以MPEG-2传输流格式输出ts切片文件，可以与所有HLS版本兼容。
                 * 'fmp4':以Fragmented MP4(简称：fmp4)格式输出切片文件，类似于MPEG-DASH，fmp4文件可用于HLS version 7和更高版本。
                 */
                  recorder.setOption("hls_segment_type","mpegts");
                AVFormatContext fc = null;
                fc = grabber.getFormatContext();
                recorder.start(fc);
                // 清空探测时留下的缓存
                AVPacket pkt = null;
                //grabber.flush();
                long dts = 0, pts = 0;// pkt的dts、pts时间戳
                long spull_start = System.currentTimeMillis();
                while ((pkt = grabber.grabPacket()) != null) {
                    if(System.currentTimeMillis()-lastPlayTime>duration){
                        //请求没有了，该结束转换了
                        break;
                    }
                    if (pkt == null || pkt.size() <= 0 || pkt.data() == null) {
                        System.out.println("no data error:");
                        continue;
                    }
                    if (pkt.dts() == avutil.AV_NOPTS_VALUE && pkt.pts() == avutil.AV_NOPTS_VALUE || pkt.pts() < dts) {
                        //|| pkt.pts() < dts
                        logger.debug("异常pkt   当前pts: " + pkt.pts() + "  dts: " + pkt.dts() + "  上一包的pts： " + pts + " dts: "
                                + dts);
                       // err_index++;
                         av_packet_unref(pkt);
                        continue;
                    }
                    // 更新上一pkt的dts，pts
                    // 矫正dts，pts
                    pkt.pts(pts);
                    pkt.dts(dts);
                   // filter.push(pkt);

                    //取出过滤器合并后的图像
                 //   Frame filterFrame=filter.pullImage();
                    long spull_end = System.currentTimeMillis();
                    if((spull_end-spull_start)>500)
                        logger.info("pull stream take up time : {}",spull_end-spull_start);
                   // long spush_start = System.currentTimeMillis();
                    recorder.recordPacket(pkt);
                  //  long spush_end = System.currentTimeMillis();
                    //logger.info("推流耗时：{}",spush_end-spush_start);基本都是0
                    timebase = grabber.getFormatContext().streams(pkt.stream_index()).time_base().den();
                    pts += (timebase / (int) framerate);
                    dts += (timebase / (int) framerate);
                   // grabber.flush();
                    spull_start = System.currentTimeMillis();

                }


        } catch (Exception e) {
           logger.error(e.getMessage(), e);
           // logger.info("播放失败");
            //可以在这里写个错误图片流返回，这样播放器就能看到问题了
        } finally {
            logger.error("转换结束，清理文件");
            try{
                grabber.close();
                recorder.close();
            }catch (Exception e){

            }
            File file=new File(m3u8FileDir);
            try {
                FileUtils.deleteDirectory(file);
                TaskMaager.removeTask(video.getId());

            }catch (Exception ee){
                ee.printStackTrace();
            }

        }
    }
}
